package org.geektimes.injection.context.Initializer;


import org.geektimes.common.util.ResponseHandlerUtil;
import org.geektimes.injection.annotation.ResponseJson;
import org.geektimes.injection.component.ComponentBean;
import org.geektimes.injection.component.ControllerComponentBean;
import org.geektimes.injection.context.ComponentContext;
import org.geektimes.injection.stream.ResponseHandlerConsumer;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

import static java.util.Arrays.asList;

/**
 * @FileName: ControllerComponentInitializer
 * @Description: Controller初始化器
 * @Author: MaWenHao
 * @Created: 2021/3/9 - 下午10:08
 */
public class ControllerComponentInitializer implements ComponentInitializer {

    /**
     * 请求路径和 {@link ControllerComponentBean} 映射关系缓存
     */
    private Map<String, ControllerComponentBean> handleMethodInfoMapping = new HashMap<>();

    /**
     * 请求路径和 Controller 的映射关系缓存
     */
    private Map<String, Object> controllersMapping = new HashMap<>();

    /**
     * 判断传入的Class对象是否可以被该初始化器初始化
     *
     * @param aClass
     * @return
     */
    @Override
    public boolean match(Class<?> aClass) {
        Path pathFromClass = aClass.getAnnotation(Path.class);
        if (Objects.nonNull(pathFromClass)) {
            return true;
        }
        return false;
    }

    /**
     * 初始化
     *
     * @param aClass
     */
    @Override
    public void init(Class<?> aClass, ComponentContext componentContext) throws Exception {
        Object instance = aClass.newInstance();
        componentContext.putComponent(aClass.getSimpleName(),
                new ComponentBean(instance));

        // 获取类上标注的路径
        Path pathFromClass = aClass.getAnnotation(Path.class);
        String prefixPath = "";
        if (Objects.nonNull(pathFromClass)) {
            prefixPath = pathFromClass.value();
        }

        ResponseJson responseJson = aClass.getAnnotation(ResponseJson.class);
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            // 获取请求全路径
            Path pathFromMethod = method.getAnnotation(Path.class);
            String requestPath = prefixPath;
            if (Objects.nonNull(pathFromMethod)) {
                requestPath += pathFromMethod.value();
            }

            // 获取应答处理器
            ResponseHandlerConsumer<Object> responseHandler = findResponseHandler(method, responseJson);

            Set<String> supportMethodNames = findSupportMethod(method);
            componentContext.putControllerComponent(requestPath,
                    new ControllerComponentBean(requestPath, method, supportMethodNames, responseHandler, instance));

        }
    }

    /**
     * 获取应答处理器
     *
     * @param method       处理器对象
     * @param responseJson 应答处理方式
     * @return 应答处理器
     */
    private ResponseHandlerConsumer<Object> findResponseHandler(Method method, ResponseJson responseJson) {
        ResponseHandlerConsumer<Object> responseHandler = ResponseHandlerUtil::responseJsonHandle;
        if (Objects.isNull(responseJson)) {
            ResponseJson jsonFromMethod = method.getAnnotation(ResponseJson.class);
            if (Objects.isNull(jsonFromMethod)) {
                responseHandler = ResponseHandlerUtil::responsePageHandle;
            }
        }
        return responseHandler;
    }

    /**
     * 查找支持的http方法名称
     *
     * @param method 方法对象
     * @return 支持的方法名称集合
     */
    private Set<String> findSupportMethod(Method method) {
        Set<String> supportMethodNames = new HashSet<>();
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
            HttpMethod httpMethod = annotation.annotationType().getAnnotation(HttpMethod.class);
            if (Objects.nonNull(httpMethod)) {
                supportMethodNames.add(httpMethod.value());
            }
        }
        if (supportMethodNames.isEmpty()) {
            supportMethodNames.addAll(asList(HttpMethod.GET, HttpMethod.POST,
                    HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS));
        }
        return supportMethodNames;
    }

}
